package handlers

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	redis2 "github.com/go-redis/redis/v7"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strconv"
	"sync"
	"time"
	"ttsApp/internal/config"
	"ttsApp/internal/models"
	"ttsApp/internal/redis"
	"ttsApp/internal/utils"
)

var removeOldRecordsLockKey = "gbes_tts_cloud_remove_old_records_lock_key"
var ttsJobList = "gbes_tts_cloud_job_list"
var ttsJobLockKey = "gbes_tts_cloud_job_lock"
var ttsJobMessageChain = "gbes_tts_cloud_chanel"

func TTSAsyncApiHandel(c *gin.Context) {
	var req models.TTSJobRecord
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	text := req.Text
	if len(text) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "text can't be empty"})
		return
	}
	textBytes, err := base64.StdEncoding.DecodeString(text)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "text DecodeString err"})
		return
	}
	text = string(textBytes)
	req.Text = text
	md5 := utils.ComputeMD5(req.Text)
	existingRecord := models.GetRequestByMD5(md5)
	if existingRecord != nil {
		existingRecord.Callback = req.Callback
		handleSuccess(existingRecord)
		c.JSON(http.StatusOK, gin.H{"success": true, "message": "Callback sent"})
		return
	}
	record := models.TTSJobRecord{
		Text:     req.Text,
		MD5:      md5,
		Speed:    req.Speed,
		Status:   0,
		Callback: req.Callback,
		Output:   "",
	}

	err = models.InsertRequest(&record)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "InsertRequest err"})
		return
	}

	req.ID = record.ID
	err = PublishMessage(&req)
	if err != nil {
		log.Printf("发布消息失败: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "PublishMessage err"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"success": true, "message": "Request created and message published"})
}

func PublishMessage(req *models.TTSJobRecord) error {
	msg, err := json.Marshal(req)
	if err != nil {
		return err
	}
	//log.Printf("添加语言转换任务")
	err = redis.ExecRedisSyncJob(ttsJobLockKey, func() error {
		err = redis.LPush(ttsJobList, string(msg))
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return err
	}
	//log.Printf("发布消息 %s", string(msg))
	_, err = redis.Publish(ttsJobMessageChain, "do_tts")
	return err
}

func StartMessageConsumer() {
	go func() {
		log.Println("开始监听语音转换任务消息频道")
		pubSubscribe := redis.Client.Subscribe(ttsJobMessageChain)
		defer func(pub *redis2.PubSub) {
			err := pub.Close()
			if err != nil {
				log.Printf("监听语音转换任务消息频道出现错误: %v", err)
			}
		}(pubSubscribe)
		for {
			msg, err := pubSubscribe.ReceiveMessage()
			log.Printf("pubSubscribe.ReceiveMessage:%v", msg)
			if err != nil {
				log.Printf("接收消息出现错误: %v", err)
			}
			message := msg.Payload
			if message == "do_tts" {
				var recordReq models.TTSJobRecord
				jobCount := int64(0)
				err = redis.ExecRedisSyncJob(ttsJobLockKey, func() error {
					jobCount, err = redis.Client.LLen(ttsJobList).Result()
					if err != nil {
						return err
					}
					if jobCount == 0 {
						log.Printf("任务队列为空，等待任务入队...")
						return nil
					}
					job, e := redis.RPop(ttsJobList)
					if e != nil {
						return e
					}
					log.Printf("出队语音转换任务: %v", job)
					recordReq = models.TTSJobRecord{}
					return json.Unmarshal([]byte(job), &recordReq)
				})

				if err == nil && jobCount > 0 && recordReq.Status == 0 {
					handleMessage(&recordReq)
				}
				if err != nil {
					log.Printf("出队语音转换任务失败: %v", err)
				}
			}
		}
	}()
}

var mutex sync.Mutex

func handleMessage(req *models.TTSJobRecord) {
	md5 := utils.ComputeMD5(req.Text)
	existingRecord := models.GetRequestByMD5(md5)

	if existingRecord != nil && existingRecord.Status == 2 {
		existingRecord.Callback = req.Callback
		handleSuccess(existingRecord)
		return
	}

	if err := models.UpdateRequestStatus(int64(req.ID), 1, "", 0); err != nil {
		log.Printf("更新任务状态失败: %v", err)
	}
	engine := config.C.Server.TTSEngine
	var cmd string
	if engine == "ekho" {
		cmd = fmt.Sprintf("./ekho -s %d -o /data/tts_output/%d.wav \"%s\" ", req.Speed, req.ID, req.Text)
	} else if engine == "coqui-tts" {
		cmd = fmt.Sprintf("tts --model_path /data/models/zh/model_file.pth --config_path /data/models/zh/config.json --text \"%s\" --out_path /data/tts_output/%d.wav", req.Text, req.ID)
	} else {
		handleError(req, "不支持"+engine+"引擎")
		return
	}

	mutex.Lock()
	log.Printf("开始执行文本转音频任务，文本：: %v", req.Text)
	processBegin := time.Now()
	output, err := exec.Command("/bin/bash", "-c", cmd).CombinedOutput()
	processEnd := time.Now()
	log.Printf("文本转音频任务执行结果: %v , 错误: %v ,耗时: %v ms", string(output) == "", err, processEnd.Sub(processBegin).Milliseconds())
	mutex.Unlock()
	if err != nil {
		handleError(req, err.Error())
		return
	}
	req.Output = fmt.Sprintf("/data/tts_output/%d.wav", req.ID)
	req.Duration = processEnd.Sub(processBegin).Milliseconds()
	handleSuccess(req)
	//删除旧文件
	clearOldFiles()
}

func clearOldFiles() {
	go func() {
		_ = redis.ExecRedisSyncJob(removeOldRecordsLockKey, func() error {
			records := models.FindOldRecords()
			for _, record := range records {
				//移除文件
				log.Printf("移除旧文件: %s", record.Output)
				err := os.Remove(record.Output)
				if err != nil {
					continue // 文件不存在，忽略错误
				}
			}
			models.RemoveOldRecords()
			return nil
		},
		)
	}()
}
func callback(arg any, callbackUrl string) {
	go func() {
		jsonResp, _ := json.Marshal(arg)
		log.Printf("回调 %s 参数: %v", callbackUrl, string(jsonResp))
		post, err := utils.DoPost(callbackUrl, bytes.NewBuffer(jsonResp))
		if err != nil {
			log.Printf("回调 %s 失败: %v", callbackUrl, err)
			return
		} else {
			log.Printf("回调  %s  成功 响应: %v", callbackUrl, post)
		}

	}()
}
func handleError(req *models.TTSJobRecord, errMsg string) {
	if err := models.UpdateRequestStatus(int64(req.ID), -1, "", 0); err != nil {
		log.Printf("更新任务状态失败: %v", err)
	}
	arg := map[string]interface{}{
		"success": false,
		"data":    nil,
		"message": errMsg,
		"code":    500,
	}
	callback(&arg, req.Callback)
}

func handleSuccess(req *models.TTSJobRecord) {
	if err := models.UpdateRequestStatus(int64(req.ID), 2, req.Output, req.Duration); err != nil {
		log.Printf("更新任务状态失败: %v", err)
	}
	arg := map[string]interface{}{
		"success": true,
		"data": map[string]string{
			"output":   req.Output,
			"duration": strconv.FormatInt(req.Duration, 10),
		},
		"code":    200,
		"message": "",
	}
	callback(&arg, req.Callback)
}
